package org.example.study.tcp;

import org.junit.Test;

import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * @descriptions: 从客户端发送文件到服务端，服务端收到文件后，返回“接收请求成功”给客户端
 * @author: zhangfaquan
 * @date: 2021/8/29 9:26
 * @version: 1.0
 */
public class TCP {

    @Test
    public void client() {
        //1.创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
        //2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
        //3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据，发送完后需要关闭这个输出流。
        //4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        //5.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
        //6.释放资源

        // 配置要连接的服务器端信息
        InetAddress inetAddress = null;
        try {
            inetAddress = InetAddress.getByName("127.0.0.1");
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        Socket socket = null;
        try {
            socket = new Socket(inetAddress, 8889);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (socket == null)
            return;

        OutputStream outputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        InputStream inputStream = null;
        FileInputStream fileInputStream = null;
        try {
            outputStream = socket.getOutputStream();
            fileInputStream = new FileInputStream("01.png");

            byte[] bytes = new byte[1024];
            int len;
            while ((len = fileInputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, len);
            }
            /*
            在这个示例中如果不不关闭输出流或就会导致阻塞，理由分析：
                客户端再写入数据给服务端时没有给结束标记，所以服务端里的while循环里的read进入了阻塞状态，变成了死循环，
                而客户端里用于等待服务端返回数据的while里的read也无法收到结束标记，所以也进入了阻塞状态，死循环。
            但是这个方法不能用于通信双方需要多次交互的情况。
            其他解决方案：
                发送数据时，约定数据的首部固定字节数为数据长度。这样读取到这个长度的数据后，就不继续调用read方法。
             */
            socket.shutdownOutput(); // 关闭输出流，不然可能导致服务端的输入流一直阻塞。发送完后调用Socket的shutdownOutput()方法关闭输出流，这样对端的输入流上的read操作就会返回-1。


            byteArrayOutputStream = new ByteArrayOutputStream();
            inputStream = socket.getInputStream();
            byte[] bytes2 = new byte[1024];
            int len2;
            while ((len2 = inputStream.read(bytes2)) != -1) {
                byteArrayOutputStream.write(bytes2, 0, len2);
            }
            System.out.println(byteArrayOutputStream.toString());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                if (outputStream != null)
                    outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void server() {
        /*
            1.创建服务器ServerSocket对象和系统要指定的端口号
            2.使用ServerSocket对象中的方法accept,获取到请求的客户端对象Socket
            3.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
            4.使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据
            5.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
            6.使用网络字节输出流OutputStream对象中的方法write,给客户端回写数据
            7.释放资源(Socket,ServerSocket)
         */

        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(8889);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (serverSocket == null)
            return;

        Socket socket = null;
        FileOutputStream fileOutputStream = null;
        InputStream inputStream = null;
        OutputStream os = null;
        try {
            socket = serverSocket.accept();
            inputStream = socket.getInputStream();
            byte[] bytes = new byte[1024];
            int len;
            // 不建议这么写，可能出现乱码
//            StringBuilder sb = new StringBuilder();
//            while ((len = inputStream.read(bytes)) != -1) {
//                sb.append(new String(bytes, 0, len));
//            }
//            System.out.println(sb);

            fileOutputStream = new FileOutputStream("02.png");
            while ((len = inputStream.read(bytes)) != -1) {
                fileOutputStream.write(bytes, 0, len);
            }
//            System.out.println(byteArrayOutputStream.toString());

            os = socket.getOutputStream();
            os.write("接收请求成功".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
